import { IGenerate } from './igenerate';
import { GenerateOption } from './generateOption';

export class DefaultGenerate implements IGenerate {
    public option:GenerateOption;
    constructor(option:GenerateOption){
        this.option = option;
    }
    generate(componentName: string, params: any, viewModel: any) {
        let first = true, that = this;


        this._touchMount(componentName, params, viewModel);
        let observableKeys = this._getObservableKeys(params);

        //配置参数存在，就进行监听
        if(params.options){ 
            //监听params的变化变化
            ko.computed(function(){
                let changeOpts = that._getChangeObservable(params,observableKeys,viewModel);

                if(first){ //如果是初始化执行，后面的业务不用重复执行了
                    first = false;
                    return;
                }
                if(changeOpts.length>0){
                    that._touchPaint(componentName,params,viewModel,changeOpts,function(){                        
                        //引起了组件重绘
                        viewModel.repaint && viewModel.repaint(params.options); 
                    });
                }
            });
        }

        return viewModel;
    }

     /**
     * 内部方法，获取配置参数(options)上的所有可监控对象的key，首次渲染组件时执行
     * @param params 组件的所有参数
     */
    _getObservableKeys(params:any):Array<string>{
        let keys = new Array<string>();
        let opts = params.options;
        if(!opts) return keys;
        
        Object.getOwnPropertyNames(opts).forEach(key=>{
            let param = opts[key];
            if(ko.isObservable(param)){
                keys.push(key);
            }
        });
        return keys;
    }

    /**
     * 获取有变更的监控对象列表
     * @param params 配置对象
     * @param observableKeys 可监控的参数列表key
     * @param viewModel 视图数据对象
     * @return 
     * {
     *  changeOpts：存在变更的对象列表
     *  reflows：可以使用回流操作来处理的变更列表
     * }
     */
    _getChangeObservable(params:any, observableKeys:Array<string>, viewModel:any):any{
        let opts = params.options; 
        let changeOpts = new Array<any>();
        observableKeys.forEach(key => {
            let param = opts[key];
            let tmp = ko.unwrap(param);
            //探测监控对象有变化的属性，区分那些可以用方法进行改变，那些需要重绘
            if(ko.isObservable(param) && param.hasChanged()){
                changeOpts.push(param);
            }
        });
        return changeOpts;
    }

    /**
     * 触发挂载方法，以及相关勾子
     * @param componentName 组件名称
     * @param params 参数列表
     * @param viewModel 视图数据对象
     */
    _touchMount(componentName:string, params:any, viewModel:any){
        let canPaint = true;
        if(viewModel.beforeMount){//绘制前的勾子
            canPaint = viewModel.beforeMount(params, componentName);
        }
        if(true || canPaint){//阻止子组件的渲染？？？怎么办
            viewModel.paint && viewModel.paint(params.options || {});
            if(viewModel.afterMount){//绘制后的勾子
                viewModel.afterMount(params, componentName);
            }
        }
    }

    /**
     * 触发组件的重绘
     * @param componentName 组件名称
     * @param params 参数列表
     * @param viewModel 视图数据对象
     * @param changeOpts 存在变更的可监控列表
     * @param action beforePaint与afterPaint之间需要执行的动作
     */
    _touchPaint(componentName:string, params:any, viewModel:any, changeOpts:any, action:any){
        let canPaint;
        if(viewModel.beforePaint){
            canPaint = viewModel.beforePaint(params, componentName, changeOpts);
        }
        if(true || canPaint){ 
            action && action();
            if(viewModel.afterPaint){
                viewModel.afterPaint(params, componentName);
            }
        }
    }
}